/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package alex;

/**
 *
 * @author Administrador
 */
public class AleXcore {



    public class ExcepcionComentarioSinCerrar extends Exception {
        public ExcepcionComentarioSinCerrar(String error){
            super(error);
        }
    }

    public class ExcepcionIdentificadorInvalido extends Exception {
        public ExcepcionIdentificadorInvalido(String error){
            super(error);
        }
    }

    public class ExcepcionComentarioCerrado extends Exception {
        public ExcepcionComentarioCerrado(String error){
            super(error);
        }
    }

    public class ExcepcionCaracterInvalido extends Exception {
        public ExcepcionCaracterInvalido(String error){
            super(error);
        }
    }

    private Buffer buf;
    private int lineadeApertura;
    private final int EOF = (char) -1;

    public AleXcore(String Archivo) throws Buffer.ExcepcionArchivoNoExiste{
        buf = new Buffer();
        buf.abrirArchivo(Archivo);
    }

    // retorna un codigo asociado al token al cual pertenece el lexema si es una palabra reservada, retorna -1
    // en caso contrario
    public int esReservada(String lexema){

        if (lexema.toLowerCase().compareTo("begin") == 0){
            return Token.BEGIN;
        }
        else if (lexema.toLowerCase().compareTo("end") == 0){
            return Token.END;
        }
        else if (lexema.toLowerCase().compareTo("div") == 0){
            //return Token.OPDIV;
            return Token.OPMULT;
        }
        else if (lexema.toLowerCase().compareTo("if") == 0){
            return Token.IF;
        }
        else if (lexema.toLowerCase().compareTo("then") == 0){
            return Token.THEN;
        }
        else if (lexema.toLowerCase().compareTo("else") == 0){
            return Token.ELSE;
        }
        else if (lexema.toLowerCase().compareTo("while") == 0){
            return Token.WHILE;
        }
        else if (lexema.toLowerCase().compareTo("function") == 0){
            return Token.FUNCTION;
        }
        else if (lexema.toLowerCase().compareTo("procedure") == 0){
            return Token.PROCEDURE;
        }
        else if (lexema.toLowerCase().compareTo("program") == 0){
            return Token.PROGRAM;
        }
        else if (lexema.toLowerCase().compareTo("const") == 0){
            return Token.CONST;
        }
        else if (lexema.toLowerCase().compareTo("var") == 0){
            return Token.VAR;
        }
        else if (lexema.toLowerCase().compareTo("type") == 0){
            return Token.TYPE;
        }
        else if (lexema.toLowerCase().compareTo("array") == 0){
            return Token.ARRAY;
        }
        else if (lexema.toLowerCase().compareTo("and") == 0){
            //return Token.OPAND;
            return Token.OPMULT;
        }
        else if (lexema.toLowerCase().compareTo("or") == 0){
            //
            return Token.OPSUMA;
        }
        else if (lexema.toLowerCase().compareTo("not") == 0){
            return Token.OPNOT;
        }
        else if (lexema.toLowerCase().compareTo("of") == 0){
            return Token.OF;
        }
        else if (lexema.toLowerCase().compareTo("do") == 0){
            return Token.DO;
        }


        return Token.IDENTIFICADOR;
    }


    public Token getToken() throws ExcepcionComentarioSinCerrar, ExcepcionIdentificadorInvalido, ExcepcionComentarioCerrado, ExcepcionCaracterInvalido {
        String Lexema = new String();
        int estado = 0;
        char caracter = buf.getChar();
        int numToken;
        int LineaDeInicioDeComentario=0;
        boolean Errores = false;

        while (! Errores){

            switch (estado) {
                // Lectura del primer caracter
                case 0: if (Character.isLetter(caracter)) { estado = 1; Lexema = Lexema + caracter;}
                        else if (caracter == EOF) {return new Token(Token.EOF, "EOF", buf.getNumeroLinea());}
                        else if (Character.isDigit(caracter)){estado = 2; Lexema = Lexema + caracter;}
                        else if (caracter == '<'){estado = 3; Lexema = Lexema + caracter; }
                        else if (caracter == '>'){estado = 4; Lexema = Lexema + caracter; }

                        else if (caracter == '+'){
                            Lexema = Lexema + caracter;
                            return new Token(Token.OPSUMA, Lexema, buf.getNumeroLinea());
                        }

                        else if (caracter == '-'){
                            Lexema = Lexema + caracter;
                            //return new Token(Token.OPRESTA, Lexema, buf.getNumeroLinea());
                            return new Token(Token.OPSUMA, Lexema, buf.getNumeroLinea());
                        }

                        else if (caracter == '*'){
                            estado = 8; // verifico si es el cierre de un comentario
                            Lexema = Lexema + caracter;
                        }

                        else if (caracter == '{'){
                            estado = 11;
                            lineadeApertura = buf.getNumeroLinea();
                            caracter = buf.getChar();
                        }

                        else if (caracter == '}'){
                            Errores = true;
                            throw new ExcepcionComentarioCerrado(Integer.toString(buf.getNumeroLinea()));
                        }

                        else if (caracter == '['){
                            Lexema = Lexema + caracter;
                            return new Token(Token.CORABRE, Lexema, buf.getNumeroLinea());
                        }

                        else if (caracter == ']'){
                            Lexema = Lexema + caracter;
                            return new Token(Token.CORCIERRA, Lexema, buf.getNumeroLinea());
                        }

                        else if (caracter == '('){
                            estado = 5; // verifico si es la apertura de un comentario
                            Lexema = Lexema + caracter;
                            caracter = buf.getChar(); 
                            break;
                        }

                        else if (caracter == ')'){
                            Lexema = Lexema + caracter;
                            return new Token(Token.PARCIERRA, Lexema, buf.getNumeroLinea());
                        }

                        else if (caracter == ','){
                            Lexema = Lexema + caracter;
                            return new Token(Token.COMA, Lexema, buf.getNumeroLinea());
                        }

                        else if (caracter == ':'){
                            estado = 9;
                            Lexema = Lexema + caracter;
                        }

                        else if (caracter == '.'){
                            estado = 10;
                            Lexema = Lexema + caracter;
                        }

                        else if (caracter == ';'){
                            Lexema = Lexema + caracter;
                            return new Token(Token.PUNTOCOMA, Lexema, buf.getNumeroLinea());
                        }

                        else if (caracter == '='){
                            Lexema = Lexema + caracter;
                            return new Token (Token.OPIGUAL, Lexema, buf.getNumeroLinea());
                        }

                        else if (Character.isWhitespace(caracter)){
                            caracter = buf.getChar(); break;
                        }

                        else {
                            Errores = true;
                            throw new ExcepcionCaracterInvalido(Integer.toString(buf.getNumeroLinea()));
                        }

                        caracter = buf.getChar();
                        break;

                case 1: /* es identificador o palabra reservada*/
                        if (Character.isLetterOrDigit(caracter)) {Lexema = Lexema + caracter;caracter = buf.getChar();}
                            else{buf.retroceder();
                                numToken = esReservada(Lexema);
                                return new Token(numToken, Lexema, buf.getNumeroLinea()); }
                        break;

                // es un numero
                case 2: if (Character.isDigit(caracter)) {Lexema = Lexema + caracter; caracter = buf.getChar();}
                        else if (Character.isLetter(caracter)) { Errores = true; throw new ExcepcionIdentificadorInvalido(Integer.toString(buf.getNumeroLinea()));  }
                        else {  buf.retroceder();
                        return new Token(Token.NUMERO, Lexema, buf.getNumeroLinea()); }
                        break;

                // simbolo anterior leido <
                case 3: if (caracter== '>') {Lexema = Lexema + caracter; caracter = buf.getChar(); numToken = Token.OPDIST; }
                        else if (caracter== '=') {Lexema = Lexema + caracter; caracter = buf.getChar(); numToken = Token.OPMENORIG; }
                        else { numToken = Token.OPMENOR; }
                            buf.retroceder();
                            return new Token(numToken, Lexema, buf.getNumeroLinea());


                // simbolo anterior leido >
                case 4: if (caracter== '=') {Lexema = Lexema + caracter; caracter = buf.getChar(); numToken = Token.OPMAYORIG; }
                        else { numToken = Token.OPMAYOR; }
                            buf.retroceder();
                            return new Token(numToken, Lexema, buf.getNumeroLinea());


                // Apertura de comentario (*
                case 5: if (caracter == '*') {  lineadeApertura=buf.getNumeroLinea();
                                                caracter = buf.getChar();
                                                LineaDeInicioDeComentario = buf.getNumeroLinea();
                                                estado=6; 
                                                }
                                else {
                                    buf.retroceder();
                                    return new Token(Token.PARABRE, Lexema, buf.getNumeroLinea());}

                //codigo para saltear comentario;
                case 6: if (caracter == '*')    { caracter = buf.getChar(); estado=7; }
                        else if (caracter == EOF) { estado = 99;}
                        else {caracter = buf.getChar(); break;}
                        

                /*pueder ser cierre de comentario*/
                case 7: if (caracter == ')')    {caracter = buf.getChar(); estado=0; Lexema = new String(); }
                        else if (caracter == '*'){caracter = buf.getChar();}
                        else if (caracter == EOF) { estado = 99;}
                        else {caracter = buf.getChar(); estado=6; }
                        break;


                // Cierre de comentario *)
                case 8: if (caracter == ')') {
                            Errores=true;
                            throw new ExcepcionComentarioCerrado(Integer.toString(buf.getNumeroLinea()));
                            }
                        else {  buf.retroceder();
                        return new Token(Token.OPMULT, Lexema, buf.getNumeroLinea());}


                // Asignacion := (el caracter anterior es un :
                case 9: if (caracter == '=') {Lexema = Lexema + caracter; caracter = buf.getChar(); numToken = Token.OPASIGN; }
                        else { numToken = Token.DOSPUNTOS; }
                        buf.retroceder();
                        return new Token(numToken, Lexema, buf.getNumeroLinea());


                // Subrango .. (el caracter anterior es un .
                case 10: if (caracter == '.') {Lexema = Lexema + caracter; caracter = buf.getChar(); numToken = Token.PUNTOPUNTO; }
                         else { numToken = Token.PUNTO; }
                         buf.retroceder();
                         return new Token(numToken, Lexema, buf.getNumeroLinea());

               //consumo el comentario
               case 11: if (caracter == '}') { caracter = buf.getChar(); estado = 0; break;}
                        else if (caracter == EOF) { Errores = true; throw new ExcepcionComentarioSinCerrar(Integer.toString(lineadeApertura));}
                        else {caracter = buf.getChar(); break;}


               case 99: Errores = true; throw new ExcepcionComentarioSinCerrar(Integer.toString(lineadeApertura));


            } /*end SWITCH*/

        }

        throw new ExcepcionComentarioSinCerrar(Integer.toString(lineadeApertura)); 


    }

}
